package com.gome.vue4j.core;

import com.gome.vue4j.common.ControllerAdapter;
import com.gome.vue4j.common.EvalUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.*;
import java.util.stream.Collectors;

public abstract class AbstractVue4jComponent implements Vue4jComponent {

    private UpdateListener listener = () -> {};

    public void addListener(UpdateListener listener) {
        this.listener = listener;
    }

    private Map<String, Object> props;

    @Override
    public void setProps(Map<String, Object> props) {
        this.props = props;
    }

    public Map<String, Object> getProps() {
        return props;
    }

    protected void update(){
        listener.update();
    }

    @Override
    public CharSequence render() {
        return new StringBuilder();
    }

    protected final Map<String,Class<?>> classes = new HashMap<>();

    public Map<String, Class<?>> imports(Class<?>... types){
        for (Class<?> type : types) {
            classes.put(type.getSimpleName(),type);
        }

        return classes;
    }

    @Override
    public Map<String, Class<?>> getImport() {
        return classes;
    }

    Document document = null;

    @Override
    public String fxml() {
        if(document == null){
            //优先使用注释的资源文件
            FXMLPath annotation = getClass().getAnnotation(FXMLPath.class);
            if(annotation != null){
                String path = annotation.value();
                try(InputStream in = getClass().getResourceAsStream(path)){
                    document = saxReader.read(in);
                }catch (IOException | DocumentException e){
                    throw new RuntimeException(e);
                }
            }else {
                //先调用import才会运行
                CharSequence render = render();
                StringBuilder builder = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\r\n\r");
                for (Class<?> value : getImport().values()) {
                    builder.append(String.format("<?import %s?>\n\r",value.getName()));
                }

                builder.append(render);
                try (StringReader reader = new StringReader(builder.toString())){
                    document = saxReader.read(reader);
                } catch (DocumentException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        Document doc = (Document) document.clone();
        Element root = doc.getRootElement();
        List<Element> elements = root.elements();
        elements.forEach(this::ifHand);
        elements.forEach(this::forHand);

        return doc.asXML();
    }

    private void ifHand(Element element) {
        List<Attribute> attributes = element.attributes();
        attributes.stream()
                .filter(e -> "if".equals(e.getName()))
                .findFirst()
                .ifPresent(attribute -> {
                    String value = attribute.getValue();
                    value = value.substring(2, value.length() - 1);

                    Object res;
                    if(value.contains("controller")){
                        res = EvalUtils.execute(value, new HashMap<String,Object>(){
                            {
                                put("controller", AbstractVue4jComponent.this);
                            }
                        });
                    }else {
                        res = tryGet(value);
                    }

                    boolean flag = Boolean.FALSE.equals(res);
                    elseHand(element,flag);

                    if(flag){
                        //不渲染
                        element.getParent().elements().remove(element);
                    }else {
                        element.attributes().remove(attribute);
                    }
                });

        element.elements().forEach(this::ifHand);
    }

    private void elseHand(Element element, boolean flag) {
        List<Element> elements = element.getParent().elements();
        int elseIndex = elements.indexOf(element) + 1;

        if(elements.size() > elseIndex){
            //存在子元素
            Element elseElement = elements.get(elseIndex);
            elseElement.attributes().stream()
                    .filter(e -> "else".equals(e.getName()))
                    .findFirst()
                    .ifPresent(e -> {
                        if(flag){
                            //渲染
                            elseElement.attributes().remove(e);
                        }else {
                            elements.remove(elseElement);
                        }
                    });
        }
    }

    private final Map<String,Object> keyValueMap = new HashMap<>();

    public Object getKeyValue(String uuid){
        return keyValueMap.get(uuid);
    }

    private void forHand(Element element) {
        element.elements().forEach(this::forHand);

        List<Attribute> attributes = element.attributes();
        attributes
                .stream()
                .filter(e -> "for".equals(e.getName()))
                .findFirst()
                .ifPresent(attribute -> {
                    //移除for属性
                    attributes.remove(attribute);

                    String[] arr = attribute.getValue().split(":");
                    String collectName = arr[1].trim();
                    String keyName = arr[0].trim();

                    Object col;
                    try{
                        col = Integer.parseInt(collectName);
                    }catch (Exception e){
                        col = tryGet(collectName);
                    }

                    List<Element> newList = new ArrayList<>();
                    if(col instanceof Integer){
                        int n = (Integer) col;
                        for (int i = 0; i < n; i++) {
                            Element clone = (Element) element.clone();
                            setAttr(keyName, i, clone);
                            newList.add(clone);
                        }
                    }else if(col instanceof List){
                        List list = (List) col;
                        for (int i = 0; i < list.size(); i++) {
                            Element clone = (Element) element.clone();
                            setAttr(keyName, i, clone);
                            newList.add(clone);
                        }
                    }else if(col instanceof Map){
                        Map map = (Map) col;
                        for (Object keyValue : map.keySet()) {
                            String uuid = UUID.randomUUID().toString();
                            keyValueMap.put(uuid,keyValue);
                            Element clone = (Element) element.clone();
                            setAttr(keyName, uuid, clone);
                            newList.add(clone);
                        }
                    }else {
                        throw new RuntimeException("for属性的collect必须是集合");
                    }

                    Element elementParent = element.getParent();
                    List<Element> elementList = elementParent.elements();
                    int index = elementList.indexOf(element);
                    elementList.remove(index);
                    element.elements().clear();

                    if("Template".equals(element.getName())){
                        newList = newList.stream()
                                .map(Element::elements)
                                .flatMap(Collection::stream)
                                .collect(Collectors.toList());
                    }

                    newList.forEach(e -> e.setParent(null));
                    elementList.addAll(index,newList);
                });

    }

    private Object tryGet(String collectName) {
        if(controllerAdapter == null){
            controllerAdapter = new ControllerAdapter(this);
        }

        return controllerAdapter.tryGet(collectName);
    }

    private void setAttr(String keyName, int keyValue, Element clone) {
        for (Element element : clone.elements()) {
            setAttr(keyName, keyValue, element);
        }

        for (Attribute attr : clone.attributes()) {
            String value = attr.getValue();
            if(isVal(value)){
                attr.setValue(attr.getValue().replace(keyName,String.valueOf(keyValue)));
            }
        }
    }

    private void setAttr(String keyName, String keyValue, Element clone) {
        for (Element element : clone.elements()) {
            setAttr(keyName, keyValue, element);
        }

        for (Attribute attr : clone.attributes()) {
            String value = attr.getValue();
            if(isVal(value)){
                attr.setValue(attr.getValue().replace(keyName,String.format("controller.getKeyValue(\"%s\")",keyValue)));
            }
        }
    }

    private boolean isVal(String value) {
        return '$' == value.charAt(0) &&
                '{' == value.charAt(1) &&
                '}' == value.charAt(value.length() - 1);
    }


    ControllerAdapter controllerAdapter;
    static SAXReader saxReader=new SAXReader();
}
